package legato.actions
{
	import legato.tasks.ExecuteAsyncMethodTask;
	
	import flash.events.Event;
	
	import mx.rpc.AbstractOperation;
	import mx.rpc.AbstractService;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	
	[Event(name="result", type="mx.rpc.events.ResultEvent")]
	[Event(name="fault", type="mx.rpc.events.FaultEvent")]
	/**
	 * Simple action that executes async method. The <code>complete</code> event is dispatched after result or fault.  
	 * Set <code>operation</code> property or <code>service</code> and <code>operationName</code> properties.
	 * <code>service</code> and <code>operationName</code> properties are chcecked if <code>operation</code> property is not set.
	 * This action uses ExcuteAsyncMethodTask. 
	 * @author Piotr
	 * 
	 */
	public class ExecuteAsyncMethodAction extends AbstractAction
	{
		private var innerTask:ExecuteAsyncMethodTask;
		
		/**
		 * Method parameters. 
		 * @param val
		 * 
		 */
		public function get params():Array
		{
			return this.innerTask.params;
		}
	
		public function set params(val:Array):void
		{
			this.innerTask.params=val;
		}
		
		/**
		 * Service that contains the operation. Use with <code>operationName</code> property.  
		 * @return 
		 * 
		 */
		public function get service():AbstractService
		{
			return this.innerTask.service;
		}
	
		public function set service(val:AbstractService):void
		{
			this.innerTask.service=val;
		}
		
		/**
		 * Name of the operation of the <code>service</code>. The <code>service</code> property must be set. 
		 * @param val
		 * 
		 */
		public function set operationName(val:String):void
		{
			this.innerTask.operationName = val;
		}

		public function get operationName():String
		{
			return this.innerTask.operationName;
		}
		
		/**
		 * Operation to execute. If this is set <code>service</code> and 
		 * <code>operationName</code> properties are not chcecked.
		 * @param val
		 * 
		 */
		public function set operation(val:AbstractOperation):void
		{
			this.innerTask.operation = val;
		}

		public function get operation():AbstractOperation
		{
			return this.innerTask.operation;
		}
		
		/**
		 * True if action was executed and not completed or cancelled. 
		 * @return 
		 * 
		 */
		public function get isRunning():Boolean
		{
			return this.innerTask.isRunning;
		}
		
		/**
		 * Constructor 
		 * 
		 */
		public function ExecuteAsyncMethodAction()
		{
			super();
			this.innerTask = new ExecuteAsyncMethodTask();
		}
				
		public override function createCopy():IAction
		{
			var actionCopy:ExecuteAsyncMethodAction = ExecuteAsyncMethodAction(super.createCopy());
			actionCopy.innerTask = this.innerTask;
			return actionCopy; 	
		}
		
		/**
		 * Stops and cancels execution of async method. The <code>complete</code> event is dispatched immediately.
		 * Works only after execution and before completition. Otherwise nothing happens. 
		 * 
		 */
		public function cancel():void
		{
			if (this.innerTask.isRunning)
			{
				this.innerTask.cancel();
				this.dispatchEvent(new Event(Event.COMPLETE));
			}
		}
		
		public override function execute():void
		{
			this.innerTask.addEventListener(ResultEvent.RESULT, this.handleResult);
			this.innerTask.addEventListener(FaultEvent.FAULT, this.handleFault);
			this.innerTask.execute();
		}
		
		private function handleResult(event:ResultEvent):void
		{
			this.innerTask.removeEventListener(ResultEvent.RESULT, this.handleResult);
			this.innerTask.removeEventListener(FaultEvent.FAULT, this.handleFault);
			this.dispatchEvent(event);
			this.dispatchEvent(new Event(Event.COMPLETE));
		}
		
		private function handleFault(event:FaultEvent):void
		{
			this.innerTask.removeEventListener(ResultEvent.RESULT, this.handleResult);
			this.innerTask.removeEventListener(FaultEvent.FAULT, this.handleFault);
			this.dispatchEvent(event);
			this.dispatchEvent(new Event(Event.COMPLETE));
		}
	}
}